package com.gitee.chili.leisure;

import com.gitee.chili.leisure.exception.BusinessException;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * 通用返回值处理类
 * <p>
 * 当不需要指定泛型，推荐使用 {@link Void} 和 {@link sun.invoke.empty.Empty}来解决 {@code NULL}
 * 它们都是表示当前返回值无数据。{@link Void} 表示更加清晰，而且唯一能持有的变量就是 {@code NULL}
 * 当需要用到反射等操作时，可用于对返回值的判断，无实际意义；{@link sun.invoke.empty.Empty} 表示
 * 一个空的类，没有任何方法和属性，但通过{@code class} 获取到的对象地址任然有意义，所以当返回值没
 * 有反射等方式的操作时使用，可能更好。
 * <p>
 * 定义一个返回类型为 {@link Void} 的示例：
 * <pre>
 *     public ResponseResult<Void> execute(){
 *
 *     }
 * </pre>
 * <p>
 * 通过这样的方式返回值也是 {@code NULL}，只是保证了泛型的安全，避免空指针
 *
 * @see ResponseCode 响应码说明
 */

@Data
@Accessors(chain = true)
public class ResponseResult<T> implements Serializable {

    private Integer code;
    private String message;
    private T data;

    public static <T> ResponseResult<T> success() {
        return success(null);
    }

    public static <T> ResponseResult<T> timeout() {
        return failure(ResponseCode.TIMEOUT);
    }

    public static <T> ResponseResult<T> success(T data) {
        return result(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getValue(), data);
    }

    public static <T> ResponseResult<T> failure() {
        return failure(ResponseCode.FAIL);
    }

    public static <T> ResponseResult<T> failure(String message) {
        return failure(ResponseCode.FAIL.getCode(), message);
    }

    public static <T> ResponseResult<T> failure(ResponseCode result) {
        return failure(result.getCode(), result.getValue());
    }

    public static <T> ResponseResult<T> result(ResponseCode code, T data) {
        return result(code.getCode(), code.getValue(), data);
    }

    public static <T> ResponseResult<T> exception(Exception ex) {
        if (ex instanceof BusinessException) {
            BusinessException gEx = (BusinessException) ex;
            return failure(gEx.getCode(), gEx.getMessage());
        }
        return failure(ex.getMessage());
    }

    public static <T> ResponseResult<T> failure(int code, String message) {
        return new ResponseResult<T>()
                .setCode(code)
                .setMessage(message);
    }

    public static <T> ResponseResult<T> errorSign() {
        return failure(ResponseCode.SING_FAIL);
    }

    public static <T> ResponseResult<T> errorSign(String message) {
        return failure(ResponseCode.SING_FAIL.getCode(), message);
    }

    private static <T> ResponseResult<T> result(Integer code, String message, T data) {
        return new ResponseResult<T>()
                .setCode(code)
                .setMessage(message)
                .setData(data);
    }

    private ResponseResult() {

    }

}